﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using AiFrame.InterfaceLib.MVP;
using AiFrame.InterfaceLib.Windows.Controls;

namespace AiFrame.InterfaceLib.Data.Patterns
{
    /// <summary>
    /// A window manager which implements the <see cref="IControllableDataWindowManager{T}"/> interface.
    /// </summary>
    public class ControllableDataWindowManager<T>: IControllableDataWindowManager<IControllableDataWindowEx<T>> 
    {
        /// <summary>
        /// The window manager instance.
        /// </summary>
        private static ControllableDataWindowManager<T> _instance;

        /// <summary>
        /// The parent control.
        /// TODO: Remove the windows control.
        /// </summary>
        protected Control _parentControl;

        /// <summary>
        /// The window collection.
        /// </summary>
        private IObservableList<IControllableDataWindowEx<T>> _wndCollection;

        /// <summary>
        /// The navigation bar.
        /// </summary>
        private IDataNavigationBar _navigationBar;

        /// <summary>
        /// The provider for the data source.
        /// </summary>
        private IDataSourceProvider<T> _dataSourceProvider;

        /// <summary>
        /// Initializes the window manager.
        /// </summary>
        /// <param name="parentControl">The parent control.</param>
        /// <param name="navigationBar">The navigation bar.</param>
        /// <param name="dataSourceProvider">The data source provider.</param>
        public void Initialize(Control parentControl, IDataNavigationBar navigationBar, IDataSourceProvider<T> dataSourceProvider)
        {
            _parentControl = parentControl;
            _navigationBar = navigationBar;
            _dataSourceProvider = dataSourceProvider;
        }
        
        /// <summary>
        /// The constructor.
        /// </summary>
        public ControllableDataWindowManager()
        {
            _wndCollection = new ObservableList<IControllableDataWindowEx<T>>();
        }

        #region Implementation of IWindowManager<IControllableDataWindow>

        /// <summary>
        /// Registers a new window.
        /// </summary>
        /// <param name="window">The data window.</param>
        public void RegisterWindow(IControllableDataWindowEx<T> window)
        {
            if (_parentControl == null)
                throw new MissingInitialisationException("Der WindowManager muss zuerst initialisiert werden.");

            _wndCollection.Add(window);
            window.SetParentWindow(_parentControl);

            _wndCollection.Sort(new ControllableDataWindowComparer());
            OnActiveWindowChanged(window);
        }

        /// <summary>
        /// Closes the given window.
        /// </summary>
        /// <param name="window">The window.</param>
        public void CloseWindow(IControllableDataWindowEx<T> window)
        {
            _wndCollection.Remove(window);
            OnActiveWindowChanged(window);
        }

        /// <summary>
        /// Determines if there is already a window of
        /// the given type.
        /// </summary>
        /// <typeparam name="W">The type of the window.</typeparam>
        /// <returns>True, if a window exists already.</returns>
        public bool ExistsWindow<W>()
        {
            if (GetWindowTypeIndex<W>() == -1)
                return false;

            return true;
        }

        /// <summary>
        /// Gets the index of the window with the given type.
        /// </summary>
        /// <typeparam name="W">The type of the window.</typeparam>
        /// <returns>The index of the window if it exists. -1 if there is not a window of this type.</returns>
        private int GetWindowTypeIndex<W>()
        {
            for (int i = 0; i < _wndCollection.Count; i++)
            {
                /* Falls der Typ existiert, true zurückgeben! */
                if (_wndCollection[i] is W)
                {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// Brings the window with the given type to the front.
        /// </summary>
        /// <typeparam name="W">The type of the window.</typeparam>
        public void BringWindowToFront<W>()
        {
            int index = GetWindowTypeIndex<W>();

            if (index != -1)
            {
                _wndCollection[index].BringWindowToFront();
                OnActiveWindowChanged(_wndCollection[index]);
            }
        }

        /// <summary>
        /// Returns the active window.
        /// </summary>
        public IControllableDataWindowEx<T> ActiveWindow
        {
            get { throw new System.NotImplementedException(); }
        }

        /// <summary>
        /// This event handler notifies when the active window has changed.
        /// </summary>
        public event EventHandler<ActiveWindowChangedParams<IControllableDataWindowEx<T>>> ActiveWindowChanged;

        /// <summary>
        /// This event handler notifies before the active window is changing.
        /// </summary>
        public event EventHandler<ActiveWindowChangedParams<IControllableDataWindowEx<T>>> ActiveWindowChanges;

        /// <summary>
        /// The method for the window changed event handler.
        /// </summary>
        /// <param name="window"></param>
        protected void OnActiveWindowChanged(IControllableDataWindowEx<T> window)
        {
            if (ActiveWindowChanged != null)
                ActiveWindowChanged(this, new ActiveWindowChangedParams<IControllableDataWindowEx<T>>(window));
        }

        #endregion

        #region Implementation of IControllableDataWindowManager<IControllableDataWindow>

        /// <summary>
        /// Registers a new window.
        /// </summary>
        /// <param name="buttonName">The name of the button.</param>
        /// <param name="buttonCaption">The caption of the button.</param>
        /// <param name="normalImage">The image of the button.</param>
        /// <param name="hoveredImage">The hovered image of the button.</param>
        /// <param name="window">The data window which will be connected to the button.</param>
        public void RegisterWindow(string buttonName, string buttonCaption, Image normalImage, Image hoveredImage, IControllableDataWindowEx<T> window)
        {
            if (_navigationBar == null)
                throw new MissingInitialisationException("Der WindowManager muss zuerst initialisiert werden.");

            _navigationBar.AddButton(buttonName, buttonCaption, normalImage, hoveredImage, OnDataNavBarClickProcedure, window, window.GetPositionIndex());
            window.SetParentWindow(_parentControl);
            window.SetDataSource(_dataSourceProvider.GetDataSource());
            window.RefreshView();

            /* TODO: Ist das gut, dass das Fenster einmal in der NavBar registriert wird
             * und hier nochmals gespeichert wird ? */
            _wndCollection.Add(window);

            _wndCollection.Sort(new ControllableDataWindowComparer());

            /* Das Basisdaten-Fenster soll wieder in den Vordergrund geholt werden,
             * da es durch das neue verdeckt wird. */
            foreach (IControllableDataWindowEx<T> dataWindowEx in _wndCollection)
            {
                if (dataWindowEx.GetPositionIndex() == 0)
                {
                    dataWindowEx.BringWindowToFront();
                    break;
                }
            }
            OnWindowRegistered(window);
            OnActiveWindowChanged(window);
        }

        /// <summary>
        /// Calls the save method of all data windows.
        /// </summary>
        public void SaveDataWindows()
        {
            foreach (IControllableDataWindowEx<T> wnd in _wndCollection)
            {
                wnd.Save();
            }
        }

        /// <summary>
        /// The window registered event.
        /// </summary>
        public event EventHandler<WindowRegisteredEventArgs<IControllableDataWindowEx<T>>> WindowRegistered;

        /// <summary>
        /// Gets all windows.
        /// </summary>
        /// <returns>The windows.</returns>
        public IList<IControllableDataWindowEx<T>> GetWindows()
        {
            return _wndCollection;
        }

        /// <summary>
        /// Method for the window-registered event.
        /// </summary>
        /// <param name="window">The window.</param>
        protected virtual void OnWindowRegistered(IControllableDataWindowEx<T> window)
        {
            if (WindowRegistered != null)
                WindowRegistered(this, new WindowRegisteredEventArgs<IControllableDataWindowEx<T>>(window));
        }

        #endregion

        /// <summary>
        /// The click procedure for the registering process with the connected
        /// data window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDataNavBarClickProcedure(object sender, EventArgs e)
        {
            if (sender is IDataNavigationBarButton)
            {
                IControllableDataWindow<T> wnd = (IControllableDataWindow<T>) (sender as IDataNavigationBarButton).GetSaveObject();
                wnd.BringWindowToFront();
            }
        }

        /// <summary>
        /// Dieser Comparer wird benötigt, um die DataWindows zu sortieren.
        /// Befehle wie "Save" müssen in einer bestimmten Reihenfolge ausgeführt werden, 
        /// sonst ergeben sich Fehler: Das Basis-Datenfenster muss z.B. immer zuerst das
        /// Datenobjekt speichern.
        /// </summary>
        public class ControllableDataWindowComparer: IComparer<IControllableDataWindowEx<T>>
        {
            #region Implementation of IComparer<IControllableDataWindowEx<T>>

            public int Compare(IControllableDataWindowEx<T> x, IControllableDataWindowEx<T> y)
            {
                if (x.GetPositionIndex() < y.GetPositionIndex())
                    return -1;

                if (x.GetPositionIndex() > y.GetPositionIndex())
                    return 1;

                return 0;
            }

            #endregion
        }

        #region Implementation of IShortCuts

        /// <summary>
        /// The shortcut for refreshing the view.
        /// </summary>
        public void RefreshView()
        {
            throw new System.NotImplementedException();
        }

        #endregion
    }
}
